package io.kiki.sba.registry.server.metadata.resource;


import io.kiki.sba.registry.common.model.GenericResponse;
import io.kiki.sba.registry.common.model.Response;
import io.kiki.sba.registry.common.model.console.PersistenceDataBuilder;
import io.kiki.sba.registry.common.model.metaserver.MultiClusterSyncInfo;
import io.kiki.sba.registry.server.shared.resource.AuthChecker;
import io.kiki.sba.registry.store.api.meta.MultiClusterSyncRepository;
import io.kiki.sba.registry.util.StringFormatter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;


@Path("api/multi/cluster")
@Produces(MediaType.APPLICATION_JSON)
public class MultiClusterSyncResource {

    private static final Logger logger = LoggerFactory.getLogger(MultiClusterSyncResource.class);

    @Autowired
    private MultiClusterSyncRepository multiClusterSyncRepository;

    @GET
    @Path("query")
    @Produces(MediaType.APPLICATION_JSON)
    public GenericResponse<MultiClusterSyncInfo> query(@QueryParam("remoteDataCenter") String remoteDataCenter) {
        GenericResponse<MultiClusterSyncInfo> response = new GenericResponse();
        if (StringUtils.isBlank(remoteDataCenter)) {
            logger.error("[query]remoteDataCenter is not allow empty.");
            return response.fillFailed("remoteDataCenter is not allow empty.");
        }
        MultiClusterSyncInfo query = multiClusterSyncRepository.query(remoteDataCenter);
        response.fillSucceed(query);
        return response;
    }

    @POST
    @Path("/save")
    @Produces(MediaType.APPLICATION_JSON)
    public Response saveConfig(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("remoteMetaAddress") String remoteMetaAddress, @FormParam("token") String token) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("save multi cluster syncs config, remoteDataCenter={}, remoteMetaAddress={}, auth check={} fail!", remoteDataCenter, remoteMetaAddress, token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }
        if (StringUtils.isBlank(remoteDataCenter) || StringUtils.isBlank(remoteMetaAddress)) {
            return Response.buildFailedResponse("remoteDataCenter, remoteMetaAddress is not allow empty.");
        }

        MultiClusterSyncInfo multiClusterSyncInfo = new MultiClusterSyncInfo(remoteDataCenter, remoteMetaAddress, PersistenceDataBuilder.nextVersion());
        multiClusterSyncInfo.setEnableSyncDatum(true);
        multiClusterSyncInfo.setEnablePush(false);
        boolean ret = multiClusterSyncRepository.insert(multiClusterSyncInfo);

        logger.info("[saveConfig]save multi cluster sync config, result:{}, remoteDataCenter:{}, remoteMetaAddress:{}", ret, remoteDataCenter, remoteMetaAddress);

        Response response = new Response();
        response.setSuccess(ret);
        return response;
    }

    @POST
    @Path("/sync/enable")
    @Produces(MediaType.APPLICATION_JSON)
    public Response syncEnable(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("expectVersion") String expectVersion, @FormParam("token") String token) {
        return updateSyncSwitch(token, remoteDataCenter, expectVersion, true);
    }

    @POST
    @Path("/sync/disable")
    @Produces(MediaType.APPLICATION_JSON)
    public Response syncDisable(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("expectVersion") String expectVersion, @FormParam("token") String token) {
        return updateSyncSwitch(token, remoteDataCenter, expectVersion, false);
    }

    private Response updateSyncSwitch(String token, String remoteDataCenter, String expectVersion, boolean enable) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("update multi cluster sync switch, remoteDataCenter={}, auth check={} fail!", remoteDataCenter, token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }

        if (StringUtils.isBlank(remoteDataCenter) || StringUtils.isBlank(expectVersion)) {
            return Response.buildFailedResponse("remoteDataCenter, expectVersion is not allow empty.");
        }

        MultiClusterSyncInfo exist = multiClusterSyncRepository.query(remoteDataCenter);

        if (exist == null || exist.getDataVersion() != Long.parseLong(expectVersion)) {
            return Response.buildFailedResponse(StringFormatter.format("remoteDataCenter:{}, expectVersion:{} not exist.", remoteDataCenter, expectVersion));
        }

        exist.setEnableSyncDatum(enable);
        exist.setDataVersion(PersistenceDataBuilder.nextVersion());
        boolean ret = multiClusterSyncRepository.update(exist, NumberUtils.toLong(expectVersion));

        logger.info("[syncSwitch]result:{}, remoteDataCenter:{}, remoteMetaAddress:{}, expectVersion:{}", ret, remoteDataCenter, expectVersion);

        Response response = new Response();
        response.setSuccess(ret);
        return response;
    }

    @POST
    @Path("/push/enable")
    @Produces(MediaType.APPLICATION_JSON)
    public Response pushEnable(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("expectVersion") String expectVersion, @FormParam("token") String token) {
        return updatePushSwitch(token, remoteDataCenter, expectVersion, true);
    }

    @POST
    @Path("/push/disable")
    @Produces(MediaType.APPLICATION_JSON)
    public Response pushDisable(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("expectVersion") String expectVersion, @FormParam("token") String token) {
        return updatePushSwitch(token, remoteDataCenter, expectVersion, false);
    }

    private Response updatePushSwitch(String token, String remoteDataCenter, String expectVersion, boolean pushEnable) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("update multi cluster push switch, remoteDataCenter={}, auth check={} fail!", remoteDataCenter, token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }

        if (StringUtils.isBlank(remoteDataCenter) || StringUtils.isBlank(expectVersion)) {
            return Response.buildFailedResponse("remoteDataCenter, expectVersion is not allow empty.");
        }

        MultiClusterSyncInfo exist = multiClusterSyncRepository.query(remoteDataCenter);

        if (exist == null || exist.getDataVersion() != Long.parseLong(expectVersion)) {
            return Response.buildFailedResponse(StringFormatter.format("remoteDataCenter:{}, expectVersion:{} not exist.", remoteDataCenter, expectVersion));
        }

        if (!exist.isEnableSyncDatum() && pushEnable) {
            logger.error("update multi cluster push switch, remoteDataCenter={}, exist={}, not allow set pushEnable=true when syncEnable is false", remoteDataCenter, exist);
            return Response.buildFailedResponse(StringFormatter.format("remoteDataCenter={}, not allow set pushEnable=true when syncEnable is false", remoteDataCenter));
        }

        exist.setEnablePush(pushEnable);
        exist.setDataVersion(PersistenceDataBuilder.nextVersion());
        boolean ret = multiClusterSyncRepository.update(exist, NumberUtils.toLong(expectVersion));

        logger.info("[pushSwitch]result:{}, remoteDataCenter:{}, remoteMetaAddress:{}, expectVersion:{}", ret, remoteDataCenter, expectVersion);

        Response response = new Response();
        response.setSuccess(ret);
        return response;
    }

    @POST
    @Path("/updateMetaAddress")
    @Produces(MediaType.APPLICATION_JSON)
    public Response updateMetaAddress(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("remoteMetaAddress") String remoteMetaAddress, @FormParam("token") String token, @FormParam("expectVersion") String expectVersion) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("update multi cluster syncs config, remoteDataCenter={}, remoteMetaAddress={}, auth check={} fail!", remoteDataCenter, remoteMetaAddress, token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }

        if (StringUtils.isBlank(remoteDataCenter) || StringUtils.isBlank(remoteMetaAddress) || StringUtils.isBlank(expectVersion)) {
            return Response.buildFailedResponse("remoteDataCenter, remoteMetaAddress, expectVersion is not allow empty.");
        }

        MultiClusterSyncInfo exist = multiClusterSyncRepository.query(remoteDataCenter);

        if (exist == null || exist.getDataVersion() != Long.parseLong(expectVersion)) {
            return Response.buildFailedResponse(StringFormatter.format("remoteDataCenter:{}, expectVersion:{} not exist.", remoteDataCenter, expectVersion));
        }

        exist.setRemoteMetaAddress(remoteMetaAddress);
        exist.setDataVersion(PersistenceDataBuilder.nextVersion());
        boolean ret = multiClusterSyncRepository.update(exist, NumberUtils.toLong(expectVersion));

        logger.info("[updateMetaAddress]result:{}, remoteDataCenter:{}, remoteMetaAddress:{}, expectVersion:{}", ret, remoteDataCenter, remoteMetaAddress, expectVersion);

        Response response = new Response();
        response.setSuccess(ret);
        return response;
    }

    @POST
    @Path("/sync/dataInfoIds/add")
    @Produces(MediaType.APPLICATION_JSON)
    public Response addSyncDataInfoIds(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("dataInfoIds") String dataInfoIds, @FormParam("token") String token, @FormParam("expectVersion") String expectVersion) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("add sync dataInfoIds, remoteDataCenter={}, dataInfoIds={}, auth check={} fail!", remoteDataCenter, dataInfoIds, token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }

        if (StringUtils.isBlank(remoteDataCenter) || StringUtils.isBlank(dataInfoIds) || StringUtils.isBlank(expectVersion)) {
            return Response.buildFailedResponse("remoteDataCenter, dataInfoIds, expectVersion is not allow empty.");
        }

        MultiClusterSyncInfo exist = multiClusterSyncRepository.query(remoteDataCenter);

        if (exist == null || exist.getDataVersion() != Long.parseLong(expectVersion)) {
            return Response.buildFailedResponse(StringFormatter.format("remoteDataCenter:{}, expectVersion:{} not exist.", remoteDataCenter, expectVersion));
        }

        exist.getSyncDataInfoIds().addAll(new HashSet<>(Arrays.asList(dataInfoIds.split(","))));
        exist.setDataVersion(PersistenceDataBuilder.nextVersion());
        boolean ret = multiClusterSyncRepository.update(exist, NumberUtils.toLong(expectVersion));

        logger.info("[addSyncDataInfoIds]result:{}, remoteDataCenter:{}, dataInfoIds:{}, expectVersion:{}", ret, remoteDataCenter, dataInfoIds, expectVersion);

        Response response = new Response();
        response.setSuccess(ret);
        return response;
    }

    @POST
    @Path("/sync/dataInfoIds/remove")
    @Produces(MediaType.APPLICATION_JSON)
    public Response removeSyncDataInfoIds(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("dataInfoIds") String dataInfoIds, @FormParam("token") String token, @FormParam("expectVersion") String expectVersion) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("remove sync dataInfoIds, remoteDataCenter={}, dataInfoIds={}, auth check={} fail!", remoteDataCenter, dataInfoIds, token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }
        if (StringUtils.isBlank(remoteDataCenter) || StringUtils.isBlank(dataInfoIds) || StringUtils.isBlank(expectVersion)) {
            return Response.buildFailedResponse("remoteDataCenter, dataInfoIds, expectVersion is not allow empty.");
        }

        MultiClusterSyncInfo exist = multiClusterSyncRepository.query(remoteDataCenter);

        if (exist == null || exist.getDataVersion() != Long.parseLong(expectVersion)) {
            return Response.buildFailedResponse(StringFormatter.format("remoteDataCenter:{}, expectVersion:{} not exist.", remoteDataCenter, expectVersion));
        }

        exist.getSyncDataInfoIds().removeAll(new HashSet<>(Arrays.asList(dataInfoIds.split(","))));
        exist.setDataVersion(PersistenceDataBuilder.nextVersion());
        boolean ret = multiClusterSyncRepository.update(exist, NumberUtils.toLong(expectVersion));

        logger.info("[removeSyncDataInfoIds]result:{}, remoteDataCenter:{}, dataInfoIds:{}, expectVersion:{}", ret, remoteDataCenter, dataInfoIds, expectVersion);

        Response response = new Response();
        response.setSuccess(ret);
        return response;
    }

    @POST
    @Path("/sync/group/add")
    @Produces(MediaType.APPLICATION_JSON)
    public Response addSyncGroup(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("group") String group, @FormParam("token") String token, @FormParam("expectVersion") String expectVersion) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("add sync group, remoteDataCenter={}, group={}, auth check={} fail!", remoteDataCenter, group, token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }

        if (StringUtils.isBlank(remoteDataCenter) || StringUtils.isBlank(group) || StringUtils.isBlank(expectVersion)) {
            return Response.buildFailedResponse("remoteDataCenter, group, expectVersion is not allow empty.");
        }

        MultiClusterSyncInfo exist = multiClusterSyncRepository.query(remoteDataCenter);

        if (exist == null || exist.getDataVersion() != Long.parseLong(expectVersion)) {
            return Response.buildFailedResponse(StringFormatter.format("remoteDataCenter:{}, expectVersion:{} not exist.", remoteDataCenter, expectVersion));
        }

        exist.getSynPublisherGroups().add(group.toUpperCase(Locale.ROOT));
        exist.setDataVersion(PersistenceDataBuilder.nextVersion());
        boolean ret = multiClusterSyncRepository.update(exist, NumberUtils.toLong(expectVersion));

        logger.info("[addSyncGroup]result:{}, remoteDataCenter:{}, group:{}, expectVersion:{}", ret, remoteDataCenter, group, expectVersion);

        Response response = new Response();
        response.setSuccess(ret);
        return response;
    }

    @POST
    @Path("/sync/group/remove")
    @Produces(MediaType.APPLICATION_JSON)
    public Response removeSyncGroup(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("group") String group, @FormParam("token") String token, @FormParam("expectVersion") String expectVersion) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("remove sync group, remoteDataCenter={}, group={}, auth check={} fail!", remoteDataCenter, group, token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }

        if (StringUtils.isBlank(remoteDataCenter) || StringUtils.isBlank(group) || StringUtils.isBlank(expectVersion)) {
            return Response.buildFailedResponse("remoteDataCenter, group, expectVersion is not allow empty.");
        }

        MultiClusterSyncInfo exist = multiClusterSyncRepository.query(remoteDataCenter);

        if (exist == null || exist.getDataVersion() != Long.parseLong(expectVersion)) {
            return Response.buildFailedResponse(StringFormatter.format("remoteDataCenter:{}, expectVersion:{} not exist.", remoteDataCenter, expectVersion));
        }

        exist.getSynPublisherGroups().remove(group.toUpperCase(Locale.ROOT));
        exist.setDataVersion(PersistenceDataBuilder.nextVersion());
        boolean ret = multiClusterSyncRepository.update(exist, NumberUtils.toLong(expectVersion));

        logger.info("[removeSyncGroup]result:{}, remoteDataCenter:{}, group:{}, expectVersion:{}", ret, remoteDataCenter, group, expectVersion);

        Response response = new Response();
        response.setSuccess(ret);
        return response;
    }

    @POST
    @Path("/remove")
    @Produces(MediaType.APPLICATION_JSON)
    public Response removeConfig(@FormParam("remoteDataCenter") String remoteDataCenter, @FormParam("expectVersion") String expectVersion, @FormParam("token") String token) {
        if (!AuthChecker.authCheck(token)) {
            logger.error("remove multi cluster syncs config, remoteDataCenter={}, auth check={} fail!", remoteDataCenter, token);
            return GenericResponse.buildFailedResponse("auth check fail");
        }

        if (StringUtils.isBlank(remoteDataCenter) || StringUtils.isBlank(expectVersion)) {
            return Response.buildFailedResponse("remoteDataCenter, expectVersion is not allow empty.");
        }

        MultiClusterSyncInfo exist = multiClusterSyncRepository.query(remoteDataCenter);

        if (exist == null || exist.getDataVersion() != Long.parseLong(expectVersion)) {
            return Response.buildFailedResponse(StringFormatter.format("remoteDataCenter:{}, expectVersion:{} not exist.", remoteDataCenter, expectVersion));
        }
        if (exist.isEnableSyncDatum()) {
            return Response.buildFailedResponse(StringFormatter.format("remove remoteDataCenter:{} sync config fail when enable sync is true.", remoteDataCenter));
        }

        int ret = multiClusterSyncRepository.remove(remoteDataCenter, NumberUtils.toLong(expectVersion));

        logger.info("[removeConfig]remove multi cluster sync config, result:{}, remoteDataCenter:{}, expectVersion:{}", ret, remoteDataCenter, expectVersion);

        Response response = new Response();
        response.setSuccess(ret > 0);
        return response;
    }
}
